﻿using System;
using System.Collections.Concurrent;
using UtilZ.Dotnet.AudioPlayer.Base;
using UtilZ.Dotnet.AudioPlayer.Core;
using UtilZ.Dotnet.AudioPlayer.Native;
using UtilZ.Dotnet.Ex.Audio;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.DataStruct;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.AudioPlayer.Player
{
    /// <summary>
    /// 流声音播放器
    /// </summary>
    unsafe public class StreamSoundPlayer : SoundPlayerAbs
    {
        /// <summary>
        /// 流式播放回调
        /// </summary>
        private static readonly STREAMPROC _streamProc;

        /// <summary>
        /// 所有流式播放器对象集合.构造函数添加到集合,Dispose从集合中移除
        /// </summary>
        private static readonly ConcurrentDictionary<int, StreamSoundPlayer> _streamSoundPlayerDic = new ConcurrentDictionary<int, StreamSoundPlayer>();

        static StreamSoundPlayer()
        {
            _streamProc = new STREAMPROC(StreamProcCallback);
        }

        private static int StreamProcCallback(int handle, IntPtr bufferPtr, int length, IntPtr user)
        {
            StreamSoundPlayer soundPlayer;
            if (_streamSoundPlayerDic.TryGetValue(handle, out soundPlayer))
            {
                return soundPlayer.GetStreamProcCallbackData(bufferPtr, length, user);
            }
            else
            {
                return 0;
            }
        }







        private readonly WaveInfo _waveInfo;
        /// <summary>
        /// 获取音频信息
        /// </summary>
        /// <returns></returns>
        protected override WaveInfo GetWaveInfo()
        {
            return this._waveInfo;
        }

        /// <summary>
        /// 获取或设置是否启用播放缓存[true:启用;false:禁用],默认为true
        /// </summary>
        public bool EnablePlayBuffer
        {
            get
            {
                return (int)BASSHelper.ChannelGetAttribute(base._handle, BASSAttribute.BASS_ATTRIB_NOBUFFER) == 0;
            }
            set
            {
                float noBuffer;
                if (value)
                {
                    noBuffer = 0f;//启用
                }
                else
                {
                    noBuffer = 1f;//禁用
                }

                BASSHelper.ChannelSetAttribute(base._handle, BASSAttribute.BASS_ATTRIB_NOBUFFER, noBuffer);
            }
        }



        private StreamPlayDataRequestLengthInsufficientArgs _playDataRequestLengthInsufficientArgs = null;
        private event EventHandler<StreamPlayDataRequestLengthInsufficientArgs> _onPlayDataRequestLengthInsufficient = null;
        /// <summary>
        /// 流播放数据请示长度不足事件
        /// </summary>
        public event EventHandler<StreamPlayDataRequestLengthInsufficientArgs> OnPlayDataRequestLengthInsufficient
        {
            add
            {
                this._onPlayDataRequestLengthInsufficient += value;
                if (this._playDataRequestLengthInsufficientArgs == null)
                {
                    this._playDataRequestLengthInsufficientArgs = new StreamPlayDataRequestLengthInsufficientArgs(base._ID);
                }
            }
            remove
            {
                this._onPlayDataRequestLengthInsufficient -= value;
            }
        }



        private StreamSoundPlayerAppendDataOverflowArgs _appendDataOverflowArgs = null;
        private event EventHandler<StreamSoundPlayerAppendDataOverflowArgs> _onAppendDataOverflow = null;
        /// <summary>
        /// 流播放追加数据溢出事件
        /// </summary>
        public event EventHandler<StreamSoundPlayerAppendDataOverflowArgs> OnAppendDataOverflow
        {
            add
            {
                this._onAppendDataOverflow += value;
                if (this._appendDataOverflowArgs == null)
                {
                    this._appendDataOverflowArgs = new StreamSoundPlayerAppendDataOverflowArgs(base._ID);
                }
            }
            remove
            {
                this._onAppendDataOverflow -= value;
            }
        }




        private readonly ValueTypeDataMerger<byte> _dataMerger;
        private readonly object _dataMergerLock = new object();
        //private int _playEndSyncHandle;
        //private readonly SYNCPROC _playEndNotifyCallback;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="id">播放器标识</param>
        /// <param name="waveInfo">音频信息</param>
        /// <param name="bufferCapcity">缓存容量,小于等于0自动计算</param>
        public StreamSoundPlayer(int id, WaveInfo waveInfo, int bufferCapcity = -1)
            : base(SoundPlayerType.Stream, id)
        {
            this._waveInfo = waveInfo;
            int bitByteCount;
            BASSFlag flags;
            switch (waveInfo.SampleBits)
            {
                case DataSampleBits.BIT8:
                    flags = BASSFlag.BASS_DEFAULT | BASSFlag.BASS_SAMPLE_8BITS;
                    bitByteCount = 1;
                    break;
                case DataSampleBits.BIT16:
                    flags = BASSFlag.BASS_DEFAULT;
                    bitByteCount = 2;
                    break;
                case DataSampleBits.BIT32:
                    flags = BASSFlag.BASS_DEFAULT | BASSFlag.BASS_SAMPLE_FLOAT;
                    bitByteCount = 4;
                    break;
                default:
                    throw new NotImplementedException($"未实现的采样类型:{waveInfo.SampleBits}");
            }

            if (bufferCapcity <= 0)
            {
                bufferCapcity = waveInfo.SampleRate * waveInfo.ChannelCount * bitByteCount * 1;//默认缓存1秒数据
            }

            this._dataMerger = new ValueTypeDataMerger<byte>(new ValueTypeDataMergerOptions() { InitCapcity = bufferCapcity });


            base._handle = BASSHelper.StreamCreate(waveInfo.SampleRate, waveInfo.ChannelCount, flags, _streamProc, IntPtr.Zero);
            if (!_streamSoundPlayerDic.TryAdd(base._handle, this))//构造函数添加到集合,Dispose从集合中移除
            {
                throw new ArgumentException($"添加流播放器对象到字典集合失败");
            }
            //this._playEndNotifyCallback = new SYNCPROC(this.PlayEndNotify);
            //this._playEndSyncHandle = BASSHelper.ChannelSetSync(this._handle, BASSSync.BASS_SYNC_FREE, -1, this._playEndNotifyCallback, IntPtr.Zero);
        }

        //private void PlayEndNotify(int handle, int channel, int data, IntPtr user)
        //{
        //    BASSHelper.BASS_ChannelRemoveSync(handle, this._playEndSyncHandle);
        //}


        /// <summary>
        /// 
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            StreamSoundPlayer soundPlayer;
            _streamSoundPlayerDic.TryRemove(base._handle, out soundPlayer);//构造函数添加到集合,Dispose从集合中移除
            this._dataMerger.Dispose();
            //BASSHelper.BASS_ChannelRemoveSync(this._playEndSyncHandle, this._playEndSyncHandle);

            //不能设置为null,因为停止播放的线程和播放线程是不同的线程,
            //如果设置为null,则当停止线程停止并释放后,播放线程还在播放则会崩溃
            //this._streamProc = null;
            //this._ringBuffer = null;
        }





        /// <summary>
        /// 自定义播放数据[参数1:请求数据长度;参数2:数据存放指针;参数3:返回请求到的数据长度]
        /// 用于播放外部缓存数据场景,当此回调有注册,则启用;为null则禁用.默认为null
        /// </summary>
        public Func<int, IntPtr, int> CustomerPlayDataFunc = null;

        unsafe private int GetStreamProcCallbackData(IntPtr bufferPtr, int reqLength, IntPtr user)
        {
            try
            {
                var func = this.CustomerPlayDataFunc;
                if (func != null)
                {
                    return func(reqLength, bufferPtr);
                }

                var dataLength = this._dataMerger.Length;
                if (reqLength > dataLength)
                {
                    //通过事件输出播放数据不足信息
                    var handler = this._onPlayDataRequestLengthInsufficient;
                    var args = this._playDataRequestLengthInsufficientArgs;
                    if (handler != null && args != null)
                    {
                        args.Set(reqLength, dataLength);
                        handler(this, args);
                        //ZLoger.Trace(base._ID, null, $"实际数据长度{dataLength}少于播放请求数据长度:{reqLength}");
                    }

                    reqLength = dataLength;
                }

                lock (this._dataMergerLock)
                {
                    this._dataMerger.Take((byte*)bufferPtr, reqLength);
                }

                return reqLength;
            }
            catch (Exception ex)
            {
                ZLoger.Error(base._ID, null, ex, "播放器获取播放数据发生异常");
                return 0;
            }
        }



        /// <summary>
        /// 获取数据长度
        /// </summary>
        public int DataLength
        {
            get
            {
                return this._dataMerger.Length;
            }
        }


        /// <summary>
        /// 检查追加数据结果
        /// </summary>
        /// <param name="appendDataLength">追加成功的数据长度</param>
        /// <param name="srcLength">用于追加数据的原始数据长度</param>
        private void CheckAppendDataResult(int appendDataLength, int srcLength)
        {
            if (appendDataLength < srcLength)
            {
                //通过事件输出追加数据溢出信息
                var handler = this._onAppendDataOverflow;
                var args = this._appendDataOverflowArgs;
                if (handler != null && args != null)
                {
                    args.Set(appendDataLength, srcLength);
                    handler(this, args);
                }
            }
        }

        /// <summary>
        /// 追加数据到播放缓存,不支持多线程
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="length">数据长度</param>
        public void AppendData(byte* data, int length)
        {
            lock (this._dataMergerLock)
            {
                int appendDataLength = this._dataMerger.Append(data, length);
                this.CheckAppendDataResult(appendDataLength, length);
            }
        }

        /// <summary>
        /// 追加数据到播放缓存,不支持多线程
        /// </summary>
        /// <param name="data">数据</param>
        public void AppendData(byte[] data)
        {
            lock (this._dataMergerLock)
            {
                int appendDataLength = this._dataMerger.Append(data);
                this.CheckAppendDataResult(appendDataLength, data.Length);
            }
        }

        /// <summary>
        /// 追加数据到播放缓存,不支持多线程
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="index">起始位置索引</param>
        /// <param name="length">数据长度</param>
        public void AppendData(byte[] data, int index, int length)
        {
            lock (this._dataMergerLock)
            {
                int appendDataLength = this._dataMerger.Append(data, index, length);
                this.CheckAppendDataResult(appendDataLength, data.Length);
            }
        }

        /// <summary>
        /// 追加数据到播放缓存,不支持多线程
        /// </summary>
        /// <param name="data">数据</param>
        public void AppendData(SpanZ<byte> data)
        {
            lock (this._dataMergerLock)
            {
                long appendDataLength = this._dataMerger.Append(data);
                this.CheckAppendDataResult((int)appendDataLength, data.Length);
            }
        }

        /// <summary>
        /// 清空缓存数据
        /// </summary>
        public void Clear()
        {
            lock (this._dataMergerLock)
            {
                this._dataMerger.Clear();
            }
        }
    }

    /// <summary>
    /// 流播放数据请示长度不足事件参数
    /// </summary>
    public class StreamPlayDataRequestLengthInsufficientArgs : EventArgs
    {
        private readonly int _id;
        /// <summary>
        /// 播放器ID标识
        /// </summary>
        public int ID
        {
            get { return this._id; }
        }

        private int _requestLength = 0;
        /// <summary>
        /// 播放请求数据长度byte
        /// </summary>
        public int RequestLength
        {
            get { return this._requestLength; }
        }


        private int _dataLength = 0;
        /// <summary>
        /// 已有数据长度byte
        /// </summary>
        public int DataLength
        {
            get { return this._dataLength; }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="id">播放器ID标识</param>
        public StreamPlayDataRequestLengthInsufficientArgs(int id)
        {
            this._id = id;
        }

        /// <summary>
        /// 设置参数
        /// </summary>
        /// <param name="requestLength">播放请求数据长度byte</param>
        /// <param name="dataLength">已有数据长度byte</param>
        public void Set(int requestLength, int dataLength)
        {
            this._requestLength = requestLength;
            this._dataLength = dataLength;
        }

    }

    /// <summary>
    /// 流播放追加数据溢出事件参数
    /// </summary>
    public class StreamSoundPlayerAppendDataOverflowArgs : EventArgs
    {
        private readonly int _id;
        /// <summary>
        /// 播放器ID标识
        /// </summary>
        public int ID
        {
            get { return this._id; }
        }

        private int _appendDataLength = 0;
        /// <summary>
        /// 追加数据长度byte
        /// </summary>
        public int AppendDataLength
        {
            get { return this._appendDataLength; }
        }


        private int _overflowDataLength = 0;
        /// <summary>
        /// 溢出数据长度byte
        /// </summary>
        public int OverflowDataLength
        {
            get { return this._overflowDataLength; }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="id">播放器ID标识</param>
        public StreamSoundPlayerAppendDataOverflowArgs(int id)
        {
            this._id = id;
        }

        /// <summary>
        /// 设置参数
        /// </summary>
        /// <param name="appendDataLength">追加数据长度byte</param>
        /// <param name="overflowDataLength">溢出数据长度byte</param>
        public void Set(int appendDataLength, int overflowDataLength)
        {
            this._appendDataLength = appendDataLength;
            this._overflowDataLength = overflowDataLength;
        }

    }

}
